/**
 * 
 */
package smallChanges.statements;

import java.util.Set;

import net.sourceforge.pmd.ast.SimpleJavaNode;
import smallChanges.Tokenization.CodeRange2D;
import smallChanges.Tokenization.SCToken;
import smallChanges.Tokenization.TokenSet;

/**
 * @author jspacco
 *
 */
public class MultiTokenCodeRange2D extends CodeRange2D
{

	protected TokenSet tokensInRange;
	protected transient TokenSet allTokensInClass;
	
	public MultiTokenCodeRange2D(TokenSet tokenSet) {
	    this.allTokensInClass=tokenSet;
    }
	
    public void setTokensFromRange()
	{
		tokensInRange=allTokensInClass.getAllTokensInRange(
				getBeginLine(), getBeginColumn(), 
				getEndLine(), getEndColumn());
	}
    
    public void setCoordinatesFromNode(SimpleJavaNode node)
    {
        super.setCoordinatesFromNode(node);
        setTokensFromRange();
    }
    
    public void setCoordinatesFromToken(SCToken first)
    {
        setBeginLine(first.getBeginLine());
        setBeginColumn(first.getBeginColumn());
        setEndLine(first.getEndLine());
        setEndColumn(first.getEndColumn());
        setTokensFromRange();
    }
    
    /**
     * Set the end line/col coordinates for the current code range based on
     * the ending line/col coordinates from the given node.  Since this method
     * completes the code range, it calls {@link #setTokensFromRange()} to collect
     * all of the tokens from the now-completed range.
     * 
     * TODO Poorly named method; refactor it!
     * @param node
     */
    protected void setEndCoordsFromNode(SimpleJavaNode node)
    {
        setEndLine(node.getEndLine());
        setEndColumn(node.getEndColumn());
        setTokensFromRange();
    }
    
    /**
     * Set the begin line/col coordinates for the current code range
     * based on the beginning line/col coordinates from the given node.
     * @param node
     */
    public void setBeginCoordsFromNode(SimpleJavaNode node)
    {
        setBeginLine(node.getBeginLine());
        setBeginColumn(node.getBeginColumn());
    }
    
    public void setBeginCoordsFromToken(SCToken token)
    {
        setBeginLine(token.getBeginLine());
        setBeginColumn(token.getBeginColumn());
    }
    
    public void setEndCoordsFromToken(SCToken token)
    {
        setEndLine(token.getEndLine());
        setEndColumn(token.getEndColumn());
        setTokensFromRange();
    }

    
    public String getTokenImage() {
        return getTokensInRange().getImage();
    }

	/**
	 * @return the tokensInRange
	 */
	public TokenSet getTokensInRange()
	{
		return tokensInRange;
	}

    public String expandByOnePrecedingToken()
    {
        TokenSet s=allTokensInClass.getPrecedingTokens(beginLine, beginColumn, endLine, endColumn);
        if (s.size()==0) {
            return null;
        }
        SCToken lastPrecedingToken=s.getLast();
        setBeginLine(lastPrecedingToken.getBeginLine());
        setBeginColumn(lastPrecedingToken.getBeginColumn());
        setTokensFromRange();
        return tokensInRange.getFirst().getImage();
    }
    
    /**
     * Expand the current range of tokens by one preceding token, but only
     * if the token is contained in the given set of expected tokens.
     * @param set
     * @return true if we were able to expand the token range, false otherwise
     */
    public boolean expandByOnePrecedingTokenConditionally(Set<String> set)
    {
        int previousBeginLine=beginLine;
        int previousBeginColumn=beginColumn;
        TokenSet s=allTokensInClass.getPrecedingTokens(beginLine, beginColumn, endLine, endColumn);
        if (s.size()==0) {
            return false;
        }
        SCToken lastPrecedingToken=s.getLast();
        setBeginLine(lastPrecedingToken.getBeginLine());
        setBeginColumn(lastPrecedingToken.getBeginColumn());
        setTokensFromRange();
        String token=tokensInRange.getFirst().getImage();
        if (set.contains(token)) {
            // this was an expected token, so return its String image
            // to signal success.
            return true;
        }
        // otherwise reset the token set back the previous begin/end line
        // and return null to indicate failure
        setBeginLine(previousBeginLine);
        setBeginColumn(previousBeginColumn);
        setTokensFromRange();
        return false;
    }

    /**
     * @deprecated Can't be used in general because the modifier tokens are not
     * always in the same order.  For example, these are both legal
     * and equivalent:
     * <p>
     * public static synchronized final void foo()
     * </p>
     * and
     * <p>
     * public synchronized static final void foo()
     * </p>
     * @param tokenImage
     * @return
     */
    public boolean expandByOnePrecedingTokenConditionally(String tokenImage)
    {
    	/* only expand if we find the expected token */
        TokenSet s=allTokensInClass.getPrecedingTokens(beginLine, beginColumn, endLine, endColumn);
        SCToken lastPrecedingToken=s.getLast();
        if( tokenImage.equals(lastPrecedingToken.getImage()))
        {
        	setBeginLine(lastPrecedingToken.getBeginLine());
        	setBeginColumn(lastPrecedingToken.getBeginColumn());
        	setTokensFromRange();
        	return true;
        }
        return false;
    }
    
    public void expandByOneFollowingToken()
    {
        TokenSet s=allTokensInClass.getFollowingTokens(beginLine, beginColumn, endLine, endColumn);
        SCToken firstFollowingToken=s.getFirst();
        setEndLine(firstFollowingToken.getEndLine());
        setEndColumn(firstFollowingToken.getEndColumn());
        setTokensFromRange();
    }
    public void shrinkByOnePrecedingToken()
    {
        SCToken first=getTokensInRange().getFirst();
        SCToken newFirst=allTokensInClass.getFollowingTokens(first).getFirst();
        setBeginLine(newFirst.getBeginLine());
        setBeginColumn(newFirst.getBeginColumn());
        setTokensFromRange();
    }
}
